﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Common.Cryptography
{
    public class SymmetricDataEncryptor : IDataEncryptor
    {
        private readonly byte[] _saltCode =
            {
                0x49,
                0x76,
                0x61,
                0x6e,
                0x22,
                0x9f,
                0x91,
                0x2e,
                0xa6,
                0x47,
                0x64,
                0x02,
                0x1b
            };

        #region IDataEncryptor implementation

        public string Encrypt(string dataToEncrypt, string key)
        {
            var clearBytes = Encoding.Unicode.GetBytes(dataToEncrypt);
            var pdb = new PasswordDeriveBytes(key, _saltCode);
            var encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
            return Convert.ToBase64String(encryptedData);
        }

        public string Decrypt(string dataToDecrypt, string key)
        {
            var cipherBytes = Convert.FromBase64String(dataToDecrypt);
            var pdb = new PasswordDeriveBytes(key, _saltCode);
            var decryptedData = Decrypt(cipherBytes,
                                        pdb.GetBytes(32), pdb.GetBytes(16));
            return Encoding.Unicode.GetString(decryptedData);
        }

        #endregion

        #region private methods

        private byte[] Encrypt(byte[] clearData, byte[] key, byte[] iv)
        {
            using (var memoryStream = new MemoryStream())
            {
                Rijndael alg = Rijndael.Create();
                alg.Key = key;
                alg.IV = iv;
                using (var cryptoStream = new CryptoStream(memoryStream, alg.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(clearData, 0, clearData.Length);
                }

                return memoryStream.ToArray();
            }
        }

        private byte[] Decrypt(byte[] cipherData,
                               byte[] key, byte[] iv)
        {
            using (var memoryStream = new MemoryStream())
            {
                Rijndael alg = Rijndael.Create();
                alg.Key = key;
                alg.IV = iv;
                using (var cryptoStream = new CryptoStream(memoryStream, alg.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(cipherData, 0, cipherData.Length);
                }

                var decryptedData = memoryStream.ToArray();
                return decryptedData;
            }
        }

        #endregion
    }
}
